Skip to content

feat(routing): Envelope 三档路由 + traceId/hop 防环 + 离线补投 (PR6)#193

Open
raysonmeng wants to merge 1 commit into
feat/v3-pr5-roomsfrom
feat/v3-pr6-routing
Open

feat(routing): Envelope 三档路由 + traceId/hop 防环 + 离线补投 (PR6)#193
raysonmeng wants to merge 1 commit into
feat/v3-pr5-roomsfrom
feat/v3-pr6-routing

Conversation

@raysonmeng

Copy link
Copy Markdown
Owner

Stacked on #192 (PR5) — base 是 feat/v3-pr5-rooms

概要 / Summary

PR6:把 broker 从纯 topic 扇出升级为身份感知三档路由 + 防环 + 防伪 + 离线补投(§3.2),消费 Store。

改动 / Changes(集中在 src/broker.ts

能力 实现
三档路由 shouldDeliver per-ws 过滤:broadcast(无 to→全发) / @提及(全发+client 高亮) / DM(to:[id]→仅命名身份;present to即 DM、[]→nobody、绝不退化广播)
loop prevention from-skip(绝不回声发送者) + hop<=0 drop
防伪 publish 入口运行期 envelope 校验(from 必对象 / to 必数组 / idempotencyKey/roomId 必非空) + 无条件 stamp from.agentId=认证身份
离线补投 store_if_offline 对不可达 intended(DM=to / broadcast=getMembers) enqueuePending;hello + subscribe 后都 drainPendingTo(覆盖「已连接未订阅」gap)
加固 双 hello 守卫(防身份重绑泄漏);drain 移出 auth try/catch;topicMembers 多连接计数

测试计划 / Test plan

  • broker-routing.test 12 用例:DM 隔离 / broadcast 跳发送者 / hop-drop / 离线补投 / 防伪 / double-hello / gap-drain / drain-error / 空-to / missing-key / SqliteStore 离线(堵 InMemoryStore 测试盲点)
  • 现有测试改双身份(同身份 pub/sub 被 from-skip 是预期行为)
  • bun run check exit 0 → 1763 pass / 0 fail、typecheck 干净、bundle 同步

Cross-review

5 轮(轮 1 thorough + 4 lean),抓出并修复 6 个真实 issue

  • HIGH 缺失/非对象 from 绕过防伪 stamp → 防环失效 + 转发未认证信封(实证:发送者收到自己广播)
  • HIGH drainPendingTo 在 auth try/catch 内 → store 错误被当认证失败 → 永久踢掉已认证连接
  • MED hello-drain 与 subscribe 间「已连接未订阅」gap → store_if_offline 既不实时也不补投
  • MED 双 hello 身份重绑 → topicMembers 泄漏 → 旧身份 DM 静默丢
  • LOW string to 退化为子串匹配 → DM 泄漏给子串身份
  • LOW idempotencyKey/roomId 未校验 → SqliteStore NOT NULL+OR IGNORE 静默丢(InMemoryStore 却投递,§6.4 跨实现分叉)

连续两轮 0 真实 issue → 收敛。

Backlog

topic==roomId 双源校验、to 元素类型、from.name/agentType 防伪、异步 Store 的 offline+live 原子性(§8.2/PR11)、BrokerClient 暴露 onError。

🤖 Generated with Claude Code

按 spec §3.2 把 broker 从纯 topic 扇出升级为身份感知路由,消费 Store(pending/getMembers)。

- 三档路由:per-ws handler 的 shouldDeliver 过滤——broadcast(无 to → 全发)、@提及(全发,
  高亮 client 侧 mentions[])、DM(to:[id] → 仅命名身份,present to 即 DM、空 to → nobody,绝不退化广播)。
- loop prevention:from-skip(绝不回声发送者) + hop<=0 drop(多 hop 泛化)。
- 防伪:publish 入口运行期 envelope 校验(from 必对象/to 必数组/idempotencyKey/roomId 必非空 string)
  + 无条件 stamp env.from.agentId=认证身份(覆盖客户端伪造)。
- 离线补投:store_if_offline 对 intended(DM=to / broadcast=getMembers) 中不可达身份 enqueuePending;
  hello(welcome) 与 subscribe 后都 drainPendingTo 补收(覆盖「已连接未订阅」gap 窗口)。
- topicMembers Map<topic,Map<id,count>> 跟踪每 topic 可达身份(支持同身份多连接)。
- 双 hello 守卫(防身份重绑泄漏 topicMembers);drain 移出 auth try/catch(防 store 错误误踢连接)。

测试:broker-routing.test 12 用例(DM 隔离/broadcast 跳发送者/hop-drop/离线补投/防伪/double-hello/
gap-drain/drain-error/空-to/missing-key/**SqliteStore 离线**堵 InMemoryStore 测试盲点);现有测试
改双身份(同身份 pub/sub 被 from-skip 是预期)。full check exit 0:1763 pass / 0 fail。

Cross-review:5 轮(轮1 thorough + 4 lean),抓出并修复 6 个真实 issue——HIGH 缺失/非对象 from 绕过
防伪+防环失效、HIGH drain 在 auth catch 内致 store 错误误踢连接、MED gap 窗口离线丢、MED 双 hello
重绑泄漏、LOW string-to 子串匹配 DM 泄漏、LOW idempotencyKey/roomId 未校验致 SqliteStore 静默丢(§6.4
跨实现分叉)。连续两轮 0 收敛。

Backlog:topic==roomId 双源校验、to 元素类型校验、from.name/agentType 防伪、异步 Store 的 offline+live
原子性(§8.2/PR11)、BrokerClient 暴露 onError、广播离线混合成员测试。

---

feat(routing): three-tier Envelope routing + traceId/hop loop prevention + offline replay (PR6)

Upgrade the broker from plain topic fan-out to identity-aware routing (§3.2):
broadcast / @mention / DM (by subscriber identity), loop prevention (never echo to
sender + hop<=0 drop), anti-spoof (runtime envelope validation + stamp the
authenticated sender), and store_if_offline replay (enqueue for unreachable
recipients, drain on (re)connect + subscribe). Converged after 5 adversarial rounds
that caught + fixed 6 real issues (2 HIGH).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant